import MQTTServer from "../libs/MQTTServer";
import MQTTClient from "../libs/MQTTClient";
import { formatterSendTopic, getMasterNodeLocalSubscriberTopics, getMasterRemoteSubscriberTopics, getNormalNodeLocalSubscriberTopics, getNormalRemoteSubscriberTopics, getRequestTopics, getResponseTopics, getTopicMapping } from "./TopicUtil";
import XLog from "../libs/XLog";
import DataCenter from "./DataCenter";
import { commonMakeMqttRequest, commonParseMqttRsp } from "./Utils";
import ProtocolSignUtils from "../common/Utils/ProtocolSignUtils";
import { ACK_Type, MessageDirection, MQTT_CONNECT_STATUS, onlineStatus, Topic_Async, Topic_Debug, Topic_Loop, Topic_Strategy, Topic_Tunnel } from "./LogicConstant";
import { DeviceEventEmitter } from "react-native";
import { topic_debug_record } from "./TopicDebugUtils";
import Config from "react-native-config";

export default class CommunicationManager {
    static localMqttClient = null;
    static remoteMqttClient = null;
    static localMqttEventHandler = {};
    static remoteMqttEventHandler = {};
    static localMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_NONE;
    static remoteMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_NONE;

    static createMqttServer() {
        return new Promise(async(resolve, reject)=>{
            MQTTServer.startMQTTService({host:"0.0.0.0"}, true, (params)=>{
                XLog.i("start server:",DataCenter.machineIp, params);
                resolve(true);
            },(param)=>{
                XLog.e("close server:", param);
                resolve(false);
            })
        });
    }

    static async destroyMqttServer() {
        await MQTTServer.stopMQTTService();
    }

    static onLocalMqttEvent(method, callback) {
        this.localMqttEventHandler[method] = callback;
    }

    static onRemoteMqttEvent(method, callback) {
        this.remoteMqttEventHandler[method] = callback;
    }

    static dispatchLocalMqttEvent(data) {
        topic_debug_record(false, false, data?.topic, data?.data);
        let topic_obj = getTopicMapping(data?.topic);
        if(topic_obj) {
            let rsp = commonParseMqttRsp(data?.data);
            if(topic_obj.direct  == MessageDirection.REQUEST) {
                if(this.localMqttEventHandler[topic_obj.method]) {
                    this.localMqttEventHandler[topic_obj.method](rsp?.params);
                } else {
                    XLog.e("no local event handler");
                }
            } else {
                if(rsp?.code == 200) {
                    if(this.localMqttEventHandler[topic_obj.method]) {
                        this.localMqttEventHandler[topic_obj.method](rsp?.data);
                    } else {
                        XLog.e("no local event handler");
                    }
                } else {
                    XLog.e("local mqtt message error:", data);
                }
            }
        } else {
            XLog.e("local unknown topic error:", data);
        }
    }

    static dispatchRemoteMqttEvent(data) {
        topic_debug_record(false, true, data?.topic, data?.data);
        let topic_obj = getTopicMapping(data?.topic);
        if(topic_obj) {
            let rsp = commonParseMqttRsp(data?.data);
            if(topic_obj.direct  == MessageDirection.REQUEST) {
                if (topic_obj.method == Topic_Debug.method || ProtocolSignUtils.validSign(rsp?.params, DataCenter.gateSecret)){
                    if(this.remoteMqttEventHandler[topic_obj.method]) {
                        this.remoteMqttEventHandler[topic_obj.method](rsp?.params);
                    } else {
                        XLog.e("no remote event handler");
                    }
                } else {
                    XLog.e("remote validSign error");
                }
            } else {
                if(rsp?.code == 200) {
                    if(this.remoteMqttEventHandler[topic_obj.method]) {
                        this.remoteMqttEventHandler[topic_obj.method](rsp?.data);
                    } else {
                        XLog.e("no remote event handler");
                    }
                } else {
                    XLog.e("remote mqtt message error:", data);
                }
            }
        } else {
            XLog.e("remote unknown topic error:", data);
        }
    }

    static async createLocalMqttClient(masterIp = DataCenter.masterIp, connectCallback) {
        if(!masterIp) {
            XLog.e('master is empty:', masterIp);
            CommunicationManager.localMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_NONE;
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_NONE);
            return -1;
        }
        this.localMqttClient = await MQTTClient.createClient("local", {
            uri:"tcp://" + masterIp + ":1883",
            clientId: "local_" + Date.now()
        });
        this.localMqttClient.on('closed', function() {
            CommunicationManager.localMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_CLOSE;
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_CLOSE);
        });
        
        this.localMqttClient.on('error', function(msg) {
            CommunicationManager.localMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_ERROR;
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_ERROR);
        });
    
        this.localMqttClient.on('message', function(msg) {
            CommunicationManager.dispatchLocalMqttEvent(msg);
        });
    
        this.localMqttClient.on('connect', async function() {
            XLog.e('local.connected');
            if(CommunicationManager.localMqttClient) {
                let topics = DataCenter.isMaster ? getMasterNodeLocalSubscriberTopics() : getNormalNodeLocalSubscriberTopics();
                if(await CommunicationManager.localMqttClient.subscribe(topics, 0)) {
                    CommunicationManager.asyncConfigDataFromMasterNow();
                    XLog.e("local subscriber success:", topics);
                } else {
                    XLog.e("local subscriber error:",topics);
                }
            } else {
                XLog.e("no local client");
            }
            CommunicationManager.localMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_SUCCESS;
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_SUCCESS);
        });
    
        return this.localMqttClient.connect();
    }

    static async destroyLocalMqttClient() {
        if(CommunicationManager.localMqttClient) {
            await MQTTClient.removeClient(CommunicationManager.localMqttClient);
        }
        CommunicationManager.localMqttClient = null;
        CommunicationManager.localMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_NONE;
    }

    static async createRemoteMqttClient(serverIp = DataCenter.serverIp, connectCallback) {
        if(!serverIp) {
            XLog.e('serverIp is empty:', serverIp);
            CommunicationManager.remoteMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_NONE;
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_NONE);
            return -1;
        }
        this.remoteMqttClient = await MQTTClient.createClient("remote", {
            uri:"tcp://" + serverIp + ":" + Config.MQTT_PORT,
            clientId: 'remote_' + Date.now()
        });
        this.remoteMqttClient.on('closed', function() {
            CommunicationManager.remoteMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_CLOSE;
            DataCenter.updateGateCurrentValue(DataCenter.gateId, "onlineStatus", onlineStatus.OFFLINE);
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_CLOSE);
            DeviceEventEmitter.emit("localCabinet");
        });
        
        this.remoteMqttClient.on('error', function(msg) {
            CommunicationManager.remoteMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_ERROR;
            DataCenter.updateGateCurrentValue(DataCenter.gateId, "onlineStatus", onlineStatus.OFFLINE);
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_ERROR);
            DeviceEventEmitter.emit("localCabinet");
        });
    
        this.remoteMqttClient.on('message', function(msg) {
            CommunicationManager.dispatchRemoteMqttEvent(msg);
        });
    
        this.remoteMqttClient.on('connect', async function() {
            XLog.e('remote.connected');
            if(CommunicationManager.remoteMqttClient) {
                let topics = DataCenter.isMaster ? getMasterRemoteSubscriberTopics() : getNormalRemoteSubscriberTopics();
                if(await CommunicationManager.remoteMqttClient.subscribe(topics, 0)) {
                    CommunicationManager.asyncConfigDataFromServerNow();
                    XLog.d("subscriber success:",topics);
                } else {
                    XLog.e("subscriber error:", topics);
                }
            } else {
                XLog.e("no remote client");
            }
            CommunicationManager.remoteMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_SUCCESS;
            DataCenter.updateGateCurrentValue(DataCenter.gateId, "onlineStatus", onlineStatus.ONLINE);
            DeviceEventEmitter.emit("localCabinet");
            connectCallback && connectCallback(MQTT_CONNECT_STATUS.CONNECT_SUCCESS);
        });
    
        return this.remoteMqttClient.connect();
    }

    static async destroyRemoteMqttClient() {
        if(CommunicationManager.remoteMqttClient) {
            await MQTTClient.removeClient(CommunicationManager.remoteMqttClient);
        }
        CommunicationManager.remoteMqttClient = null;
        CommunicationManager.remoteMqttConnectStatus = MQTT_CONNECT_STATUS.CONNECT_NONE;
    }

    static async installMqttLogic() {
        try{
            if(DataCenter.isMaster) {
                await CommunicationManager.createMqttServer();
            }
            await CommunicationManager.createLocalMqttClient();
            await CommunicationManager.createRemoteMqttClient();
        }catch(error) {
            XLog.e("install mqtt error:", error);
        }
    }

    static async uninstallMqttLogic() {
        try{
            await CommunicationManager.destroyMqttServer();

            await CommunicationManager.destroyLocalMqttClient();
            
            await CommunicationManager.destroyRemoteMqttClient();
        }catch(error) {
            XLog.e("uninstall mqtt error:", error);
        }
    }

    static send2Master(topic, payload) {
        if(CommunicationManager.localMqttClient) {
            CommunicationManager.localMqttClient.publish(topic, JSON.stringify(payload), 0, false);
        } else {
            XLog.e("no local client to publish property");
        }
        topic_debug_record(true, false, topic, payload);
    }

    static send2Remote(topic, payload) {
        if(CommunicationManager.remoteMqttClient) {
            CommunicationManager.remoteMqttClient.publish(topic, JSON.stringify(payload), 0, false);
        } else {
            XLog.e("no remote client to publish property");
        }
        topic_debug_record(true, true, topic, payload);
    }

    static asyncConfigDataFromServerNow() {
        let timerHandler = setTimeout(()=>{
            clearTimeout(timerHandler);
            if(DataCenter.isMaster) {
                CommunicationManager.send2Remote(formatterSendTopic(Topic_Loop), 
                    commonMakeMqttRequest(ACK_Type.ACK, {tunnelId: DataCenter.tunnelId, tunnelType: DataCenter.tunnelType}, Topic_Loop.method));
                CommunicationManager.send2Remote(formatterSendTopic(Topic_Tunnel), 
                    commonMakeMqttRequest(ACK_Type.ACK, {tunnelId: DataCenter.tunnelId, tunnelType: DataCenter.tunnelType}, Topic_Tunnel.method));
                CommunicationManager.send2Remote(formatterSendTopic(Topic_Strategy), 
                    commonMakeMqttRequest(ACK_Type.ACK, null, Topic_Strategy.method));
            }
        }, 5000);
    }

    static asyncConfigDataFromMasterNow() {
        let timerHandler = setTimeout(()=>{
            clearTimeout(timerHandler);
            if(!DataCenter.isMaster) {
                CommunicationManager.send2Master(formatterSendTopic(Topic_Loop), 
                    commonMakeMqttRequest(ACK_Type.ACK, null, Topic_Loop.method));
                CommunicationManager.send2Master(formatterSendTopic(Topic_Tunnel), 
                    commonMakeMqttRequest(ACK_Type.ACK, null, Topic_Tunnel.method));
                CommunicationManager.send2Master(formatterSendTopic(Topic_Strategy), 
                    commonMakeMqttRequest(ACK_Type.ACK, null, Topic_Strategy.method));
                CommunicationManager.send2Master(formatterSendTopic(Topic_Async), 
                    commonMakeMqttRequest(ACK_Type.ACK, null, Topic_Async.method));
            }
        }, 5000);
    }
}